<?php

namespace eolinker;

use eolinker\Exceptions\EolinkerException;
use eolinker\Models\EoApi;
use eolinker\Models\EoApiRequestParam;
use eolinker\Models\EoUser;
use think\facade\Db;

error_reporting(E_ERROR);

class ApiDocGenerateTool
{
    protected $userID;

    protected $project;

    /**
     * ApiDocGenerateTool constructor.
     * @param string $account eolikner账户名称
     * @param int $project 项目id,看接口文档地址栏projectID参数
     * @return ApiDocGenerateTool
     * @throws EolinkerException
     */
    public function init(string $account, int $project)
    {
        if (!$user = $this->getUser($account)) {
            throw new EolinkerException('该账户不存在');
        }
        $this->userID = $user->getKey();
        $this->project = $project;
        return $this;
    }

    /**
     * 获取用户信息
     * @param string $account
     * @return mixed
     */
    protected function getUser(string $account)
    {
        return EoUser::where('userName', $account)->find();
    }

    /**
     * 获取路由列表
     * $filters['name'] = (string)筛选路由名称
     * $filters['path'] = (string)筛选路由路径
     * $filters['method'] = (string)筛选路由请求方法
     * $filters['middleware'] = (string)筛选路由中间件
     * $filters['uri'] = (string)筛选uri
     * @param array $filters
     * @return array
     */
    public function filterRoute(array $routes, array $filters)
    {
        $list = [];
        foreach ($routes as $route) {
            $route = [
                'host' => '',
                'uri' => $route[0],
                'action' => $route[1],
                'method' => $route[2],
                'name' => $route[3],
            ];

            if (
                (!empty($filters['name']) && strpos($route['name'], $filters['name']) === false) ||
                (!empty($filters['uri']) && strpos($route['uri'], $filters['uri']) === false) ||
                (!empty($filters['action']) && strpos($route['action'], $filters['action']) === false) ||
                (!empty($filters['method']) && strpos($route['method'], mb_strtolower($filters['method'])) === false)
            ) {
                continue;
            }

            $list[] = $route;
        }
        return $list;
    }

    /**
     * @param $action
     * @return string|string[]|null
     * @throws \ReflectionException
     */
    protected function parseDoc($action)
    {
        $explodeData = explode('/', $action);
        $fn = $explodeData[count($explodeData) - 1];
        $className = '';
        foreach ($explodeData as $key => $item) {
            $item = str_replace('.', '\\', $item);

            if ($key < (count($explodeData) - 1)) {
                if (empty($className)) {
                    $className = $item;
                } else {
                    $className .= ('\\' . $item);
                }
            }
        }
        $className = 'app\\data\\controller\\' . $className;
        $class = new \ReflectionClass($className);
        $methods = $class->getMethods();
        foreach ($methods as $method) {
            if ($method->class != $className) {
                continue;
            }
            if ($method->name == $fn) {
                $doc = $method->getDocComment();
                return (new ApiDocParseTool())->parseDoc($doc);
            }
        }
    }


    /**
     * 生成文档
     * @param array $api
     * @return  bool
     * @throws \ReflectionException
     * @throws \Throwable
     */
    public function createEoliknerDoc(array $api)
    {
        $eolikner = new ApiEoLinkerTool();
        $requestMethod = EoApi::enumApiRequestType(false);
        $requestMethod = array_combine(array_column($requestMethod, 'name'), array_column($requestMethod, 'value'));
        $level = substr_count($api['name'], '.');

        if ($level < 1) {
            throw new \Exception('接口缺少name,命名规则:应用.模块.功能.name');
        }
        $method = strtolower(explode('|', $api['method'])[0] ?: 'post');
        $parseInfo = $this->parseDoc($api['action']);

        //处理目录，只支持三级目录
        $parseApiName = array_filter(explode('.', $api['name']));
        $levelDir = 4;
        if (count($parseApiName) > $levelDir) {
            $apiGroupInfo = array_slice($parseApiName, 0, $levelDir);
            $apiGroupInfo[$levelDir - 1] .= '.' . implode('.', array_slice($parseApiName, $levelDir));
        } else {
            $apiGroupInfo = $parseApiName;
        }

        //构造api
        $apiName = array_pop($apiGroupInfo);
        //        $apiName = $parseInfo['api_name'] ?: $apiName;
        //api/admin命名规则 标记为后台接口
        if (strpos($api['uri'], 'api/admin')) {
            $apiName = '后台-' . $apiName;
        }
        //获取api状态
        $apiStatus = EoApi::getApiStatus($parseInfo['status'] ?: '');
        $apiInfo['apiName'] = $apiStatus['emoji'] . $apiName;
        //创建层级菜单
        $group = $eolikner->createGroup($apiGroupInfo, $this->project);
        $apiInfo['projectID'] = $this->project;
        $apiInfo['apiURI'] = $parseInfo['api'];
        $apiInfo['apiRequestType'] = $requestMethod[$method];
        $apiInfo['groupID'] = $group->groupID;
        $apiInfo['apiStatus'] = $apiStatus[EoApi::ENUM_VALUE];
        $apiInfo['apiProtocol'] = 1;//请求协议 0=http,1=https
        if ($parseInfo['api_success_example']['content']) {
            if ($parseInfo['api_success_example']['type'] == 'json') {
                $apiInfo['apiSuccessMock'] = $parseInfo['api_success_example']['content_json'];
                $apiInfo['apiSuccessMockType'] = 1;
            } else {
                $apiInfo['apiSuccessMock'] = $parseInfo['api_success_example']['content'];
                $apiInfo['apiSuccessMockType'] = 0;
            }
        }

        if ($parseInfo['api_error_example']['content']) {
            if ($parseInfo['api_error_example']['type'] == 'json') {
                $apiInfo['apiErrorMock'] = $parseInfo['api_error_example']['content_json'];
                $apiInfo['apiErrorMockType'] = 1;
            } else {
                $apiInfo['apiErrorMock'] = $parseInfo['api_error_example']['content'];
                $apiInfo['apiErrorMockType'] = 0;
            }
        }

        $apiInfo['apiNote'] = "&lt;blockquote&gt;&lt;p&gt;最后更新时间:" . date('Y-m-d H:i:s') . "&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;" . $parseInfo['api_description'];//详细说明 文本
        $apiInfo['apiNoteRaw'] = '&gt; 最后更新时间:' . date('Y-m-d H:i:s') . PHP_EOL . PHP_EOL;
        $apiInfo['apiNoteRaw'] .= "#### 可用请求方式:" . PHP_EOL;
        foreach (explode('|', $api['method']) as $m) {
            $apiInfo['apiNoteRaw'] .= '- ' . $m . PHP_EOL;
        }
        $apiInfo['apiNoteRaw'] .= PHP_EOL . "#### 详细说明:" . PHP_EOL;
        $apiInfo['apiNoteRaw'] .= $parseInfo['api_description'];
        $apiInfo['apiNoteType'] = 1;//详细说明 文本
        //处理api数据
        $apiRes = $eolikner->saveApi($apiInfo);
        //处理请求参数
        if ($parseInfo['params']) {
            foreach ($parseInfo['params'] ?? [] as $param) {
                $requestParam['paramName'] = ($param['description'] ?: $param['field']) . ($param['allowed_values'] ? ',可选值:' . $param['allowed_values'] : '') . ',类型:' . $param['type'];
                if ($param['size']) {
                    $requestParam['paramName'] .= ',长度:' . $param['size'];
                }
                $requestParam['paramKey'] = $this->formatKey($param['field']);
                $requestParam['apiID'] = $apiRes->apiID;
                $requestParam['paramValue'] = $param['default_value'];
                $requestParam['paramType'] = $eolikner->parseParamType($param['type']);//参数类型
                $requestParam['paramLimit'] = $param['size'] ? 'size:' . $param['size'] : '';
                $requestParam['paramNotNull'] = $param['optional'] ? 1 : 0;//参数类型
                if ($param['model']) {
                    $fKey = $requestParam['paramKey'];
                    $cols = Db::name($param['model'])->getFields();
                    $modelParam = [
                        'apiID' => $requestParam['apiID'],
                        'paramLimit' => $requestParam['paramLimit'],
                        'paramNotNull' => $requestParam['paramNotNull'],
                        'paramType' => EoApiRequestParam::PARAM_TYPE_STRING[EoApiRequestParam::ENUM_VALUE],
                        'paramValue' => $requestParam['paramValue']
                    ];
                    foreach ($cols as $field => $desc) {
                        //限定字段,只需要获取指定字段即可
                        if ($param['model_field'] && !in_array($field, $param['model_field'])) {
                            continue;
                        }
                        $modelParam['paramNotNull'] = $desc['notnull'] ? 1 : 0;
                        $modelParam['paramKey'] = $this->formatKey($fKey . '.' . $field);
                        $modelParam['paramName'] = $desc['comment'];
                        $modelParam['paramType'] = $eolikner->parseParamType($desc['type']);
                        $eolikner->createRequestParam($modelParam);
                    }
                }

                $eolikner->createRequestParam($requestParam);
            }
        }
        //处理返回值
        if ($parseInfo['success']) {
            foreach ($parseInfo['success'] ?? [] as $param) {
                $resultParam['paramKey'] = $this->formatKey($param['field']);
                $resultParam['paramName'] = ($param['description'] ?: $param['field']) . ($param['allowed_values'] ? ',可选值:' . $param['allowed_values'] : '') . ',类型:' . $param['type'];
                $resultParam['apiID'] = $apiRes->apiID;
                $resultParam['paramNotNull'] = $param['optional'] ? 1 : 0;//参数类型
                //如果绑定了model 批量插入
                if ($param['model']) {
                    $fKey = $resultParam['paramKey'];
                    $cols = Db::name($param['model'])->getFields();
                    $resultModelParam = [
                        'apiID' => $resultParam['apiID'],
                        'paramNotNull' => $resultParam['paramNotNull']
                    ];
                    foreach ($cols as $field => $desc) {
                        //限定字段,只需要获取指定字段即可
                        if ($param['model_field'] && !in_array($field, $param['model_field'])) {
                            continue;
                        }
                        $resultModelParam['paramNotNull'] = $desc['notnull'] ? 1 : 0;
                        $resultModelParam['paramKey'] = $this->formatKey($fKey . '.' . $field);
                        $resultModelParam['paramName'] = $desc['comment'];
                        $resultModelParam['paramType'] = $eolikner->parseParamType($desc['type']);
                        $eolikner->createResultParam($resultModelParam);
                    }
                }
                $eolikner->createResultParam($resultParam);
            }
        }
        //生成缓存
        $eolikner->createCache($apiRes->apiID);
        return true;
    }

    /**
     * 格式化输出枚举
     * @param string $className
     * @param string $paramName
     * @return string
     */
    protected function formatEnum(string $className, string $paramName)
    {
        $values = $className::options();
        $paramName .= '(可选值:';
        foreach ($values as $v => $d) {
            $paramName .= $v . '=' . $d . ',';
        }
        $paramName = substr($paramName, 0, -1);
        $paramName .= ')';
        return $paramName;
    }

    /**
     * 将.语法转换成eolikner对象语法>>
     * @param string $key
     * @return mixed
     */
    protected function formatKey(string $key)
    {
        return str_replace('.', '>>', $key);
    }


    /**
     * 创建状态码
     * @param int $groupName
     * @param array $codeData
     * @return bool
     */
    public function createCode($groupName, array $codeData)
    {
        $tool = new ApiEoLinkerTool();
        $codeGroup = $tool->createProjectStatusCodeGroup($groupName, $this->project);
        foreach ($codeData as $code => $desc) {
            $tool->createProjectStatusCode($code, $desc, $codeGroup->getKey());
        }
        return true;
    }

}
